package com.haima.sage.bigdata.api.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.haima.sage.bigdata.api.common.Constants;
import com.haima.sage.bigdata.api.entity.AppSystem;
import com.haima.sage.bigdata.api.entity.ComputeTask;
import com.haima.sage.bigdata.api.entity.auditlog.OperateType;
import com.haima.sage.bigdata.api.service.AppSystemService;
import com.haima.sage.bigdata.api.service.AuditLogService;
import com.haima.sage.bigdata.api.service.ComputeTaskService;
import com.haima.sage.bigdata.api.entity.auditlog.ApiType;
import com.haima.sage.bigdata.api.vo.ComputeTaskVO;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.bcel.Const;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;

/**
 * 只有赋权才能访问：GetOne、ModifyData
 */
@RestController
public class ComputeController extends BaseController {
    private static final Logger LOG = LogManager.getLogger(ComputeController.class);
    private final ComputeTaskService computeTaskService;
    private final AppSystemService appSystemService;

    @Value("${com.haima.bigdata.query.api.app.id.manage.center}")
    private String manageCenterAppId;
    @Value("${com.haima.bigdata.query.api.max.batch.size}")
    private Integer maxBatchSize = 10;

    @Autowired
    public ComputeController(ComputeTaskService computeTaskService, AuditLogService auditLogService, AppSystemService appSystemService) {
        super(auditLogService);
        this.computeTaskService = computeTaskService;
        this.appSystemService = appSystemService;
    }

    @GetMapping("/api/compute/{id}")
    public ResponseEntity<String> getOne(HttpServletRequest req,
                                         @PathVariable("id") String id,
                                         @RequestParam("appkey") String appKey,
                                         @RequestParam("sign") String sign,
                                         @RequestParam("timestamp") Long timestamp,
                                         @RequestParam("appUserId") String userId,
                                         @RequestParam("appUserName") String userName) {
        try {
            ResponseEntity<String> res = validateBase(appKey, userId, userName, timestamp);
            if (res != null) {
                return res;
            }
            // 此任务是否存在
            Optional<ComputeTask> optional = computeTaskService.findById(id);
            if (!optional.isPresent()) {
                return error(HttpStatus.NOT_FOUND, Constants.NOT_FOUND_DATA_ERROR_EN,
                        Constants.NOT_FOUND_DATA_ERROR_CN);
            }
            ComputeTask task = optional.get();
            // 可以访问此计算任务的业务系统
            AppSystem bs = isForbidden(appKey, task);
            if (bs == null) {
                return error(HttpStatus.FORBIDDEN, Constants.NO_POWER_ACCESS_THIS_DATA_EN,
                        Constants.NO_POWER_ACCESS_THIS_DATA_CN);
            }
            // 签名校验
            Map<String, Object> params = new HashMap<>();
            if (!validSign(bs.getSecret(), req.getRequestURI(), params, appKey,
                    timestamp, sign, userId, userName)) {
                return error(HttpStatus.BAD_REQUEST, Constants.SIGN_ERROR_EN,
                        Constants.SIGN_ERROR_CN);
            }
            // 记录日志
            addAuditLog(appKey, bs.getName(), ApiType.Compute, OperateType.Row,
                    task.getName(), "", userId, userName);
            // 返回结果
            Map<String, Object> map = getComputeMap(task);
            return ok(map);
        } catch (Exception e) {
            LOG.error(Constants.API_COMPUTE_ERROR_EN, e);
            return error(HttpStatus.INTERNAL_SERVER_ERROR, Constants.API_COMPUTE_ERROR_EN,
                    Constants.API_COMPUTE_ERROR_CN);
        }
    }


    @RequestMapping("/api/compute")
    public ResponseEntity<String> getPager(HttpServletRequest req,
                                           @RequestParam("appkey") String appKey,
                                           @RequestParam("sign") String sign,
                                           @RequestParam("timestamp") Long timestamp,
                                           @RequestParam("appUserId") String userId,
                                           @RequestParam("appUserName") String userName,
                                           @RequestParam("from") Integer from,
                                           @RequestParam("size") Integer size) {
        try {
            ResponseEntity<String> res = validateBase(appKey, userId, userName, timestamp);
            if (res != null) {
                return res;
            }
            // 可以访问此计算任务的业务系统
            AppSystem bs = isAdminForbidden(appKey);
            if (bs == null) {
                return error(HttpStatus.FORBIDDEN, Constants.NO_POWER_ACCESS_API_EN,
                        Constants.NO_POWER_ACCESS_API_CN);
            }
            Map<String, Object> params = new HashMap<>();
            params.put("from", from);
            params.put("size", size);
            if (!validSign(bs.getSecret(), req.getRequestURI(), params, appKey,
                    timestamp, sign, userId, userName)) {
                return error(HttpStatus.BAD_REQUEST, Constants.SIGN_ERROR_EN,
                        Constants.SIGN_ERROR_CN);
            }
            // 查询分页数据
            Pageable pageable = PageRequest.of(from, size);
            Page<ComputeTask> page = this.computeTaskService.find(pageable);
            // 记录日志
            addAuditLog(appKey, bs.getName(), ApiType.Compute, OperateType.List,
                    "查询", "", userId, userName);

            // 转换格式
            List<Map<String, Object>> list = new ArrayList<>();
            Iterator<ComputeTask> iterator = page.iterator();
            while (iterator.hasNext()) {
                ComputeTask task = iterator.next();
                Map<String, Object> tmpMap = getComputeMap(task);
                list.add(tmpMap);
            }
            Map<String, Object> map = new HashMap<>();
            map.put("hits", list);
            map.put("total", page.getTotalElements());
            return ok(map);
        } catch (Exception e) {
            LOG.error(Constants.API_COMPUTE_ERROR_EN, e);
            return error(HttpStatus.INTERNAL_SERVER_ERROR, Constants.API_COMPUTE_ERROR_EN,
                    Constants.API_COMPUTE_ERROR_CN);
        }

    }


    @PostMapping(value = "/api/compute")
    public ResponseEntity<String> add(HttpServletRequest req,
                                      @RequestParam("sign") String sign,
                                      @RequestBody String json) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            ComputeTaskVO vo;
            try {
                vo = mapper.readValue(json, ComputeTaskVO.class);
            } catch (IOException e) {
                return error(HttpStatus.BAD_REQUEST, Constants.INPUT_JSON_FORMAT_ERROR_EN,
                        Constants.INPUT_JSON_FORMAT_ERROR_CN);
            }
            Long timestamp = vo.getLongTimestamp();
            ResponseEntity<String> res = validateBase(vo.getAppkey(), vo.getAppUserId(), vo.getAppUserName(), timestamp);
            if (res != null) {
                return res;
            }
            // 可以访问此接口的业务系统
            AppSystem bs = isAdminForbidden(vo.getAppkey());
            if (bs == null) {
                return error(HttpStatus.FORBIDDEN, Constants.NO_POWER_ACCESS_API_EN,
                        Constants.NO_POWER_ACCESS_API_CN);
            }

            if (!validSign(bs.getSecret(), req.getRequestURI(), json, sign)) {
                return error(HttpStatus.BAD_REQUEST,
                        "sign failure", "签名错误");
            }

            if (StringUtils.isBlank(vo.getName())) {
                return error(HttpStatus.BAD_REQUEST,
                        "compute task name must not empty", "计算任务名称不能为空");
            }
            ComputeTask task = vo.getComputeTask();
            this.computeTaskService.save(task);
            String body = mapper.writeValueAsString(task);
            addAuditLog(vo.getAppkey(), bs.getName(), ApiType.Compute, OperateType.Save,
                    task.getName(), body,
                    vo.getAppUserId(), vo.getAppUserName());

            Map<String, Object> map = getComputeMap(task);
            return ok("add success", "添加成功", map);
        } catch (Exception e) {
            LOG.error("add compute error", e);
            return error(HttpStatus.INTERNAL_SERVER_ERROR,
                    "compute task add error", "计算任务添加出错");
        }
    }

    @PutMapping(value = "/api/compute/{id}")
    public ResponseEntity<String> modify(HttpServletRequest req,
                                         @PathVariable("id") String id,
                                         @RequestParam("sign") String sign,
                                         @RequestBody String json) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            ComputeTaskVO vo;
            try {
                vo = mapper.readValue(json, ComputeTaskVO.class);
            } catch (IOException e) {
                return error(HttpStatus.BAD_REQUEST, Constants.INPUT_JSON_FORMAT_ERROR_EN,
                        Constants.INPUT_JSON_FORMAT_ERROR_CN);
            }
            ResponseEntity<String> res = validateBase(vo.getAppkey(), vo.getAppUserId(),
                    vo.getAppUserName(), vo.getLongTimestamp());
            if (res != null) {
                return res;
            }
            Optional<ComputeTask> optional = this.computeTaskService.findById(id);
            if (!optional.isPresent()) {
                return error(HttpStatus.BAD_REQUEST, Constants.NOT_FOUND_DATA_ERROR_EN,
                        Constants.NOT_FOUND_DATA_ERROR_CN);
            }
            ComputeTask task = optional.get();
            // 可以访问此接口的业务系统
            AppSystem bs = isAdminForbidden(vo.getAppkey());
            if (bs == null) {
                return error(HttpStatus.FORBIDDEN, Constants.NO_POWER_ACCESS_API_EN,
                        Constants.NO_POWER_ACCESS_API_CN);
            }
            if (!validSign(bs.getSecret(), req.getRequestURI(), json, sign)) {
                return error(HttpStatus.BAD_REQUEST, Constants.SIGN_ERROR_EN,
                        Constants.SIGN_ERROR_CN);
            }
            if (StringUtils.isBlank(vo.getName())) {
                return error(HttpStatus.BAD_REQUEST,
                        "compute task name must not empty", "计算任务名称不能为空");
            }
            ComputeTask newTask = new ComputeTask();
            newTask.setId(task.getId());
            newTask.setName(vo.getName());
            newTask.setData(vo.getData());
            newTask.setDescription(vo.getDescription());
            newTask.setUpdateDate(new Date());
            this.computeTaskService.updateData(newTask);

            String body = getModifyLogBody(mapper, task, newTask);
            addAuditLog(vo.getAppkey(), bs.getName(), ApiType.Compute, OperateType.Modify,
                    task.getName(), body, vo.getAppUserId(), vo.getAppUserName());

            Map<String, Object> map = getComputeMap(task);
            return ok("modify success", "修改成功", map);
        } catch (Exception e) {
            LOG.error(Constants.API_COMPUTE_MODIFY_ERROR_EN, e);
            return error(HttpStatus.INTERNAL_SERVER_ERROR, Constants.API_COMPUTE_MODIFY_ERROR_EN,
                    Constants.API_COMPUTE_MODIFY_ERROR_CN);
        }
    }

    private String getModifyLogBody(ObjectMapper mapper,
                                    ComputeTask oldTask, ComputeTask newTask) throws JsonProcessingException {
        Map<String, Object> auditMap = new HashMap<>();
        auditMap.put("old", oldTask);
        auditMap.put("new", newTask);
        return mapper.writeValueAsString(auditMap);
    }

    private String getModifyLogBody(ObjectMapper mapper, String data, ComputeTask task) throws JsonProcessingException {
        Map<String, String> auditMap = new HashMap<>();
        auditMap.put("data", task.getData());
        auditMap.put("new-data", data);
        return mapper.writeValueAsString(auditMap);
    }

    @PutMapping(value = "/api/compute/config/{id}")
    public ResponseEntity<String> modifyDataOnly(HttpServletRequest req,
                                                 @PathVariable("id") String id,
                                                 @RequestParam("sign") String sign,
                                                 @RequestBody String body) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            ComputeTaskVO vo;
            try {
                vo = mapper.readValue(body, ComputeTaskVO.class);
            } catch (IOException e) {
                return error(HttpStatus.BAD_REQUEST, Constants.INPUT_JSON_FORMAT_ERROR_EN,
                        Constants.INPUT_JSON_FORMAT_ERROR_CN);
            }
            ResponseEntity<String> res = validateBase(vo.getAppkey(), vo.getAppUserId(),
                    vo.getAppUserName(), vo.getLongTimestamp());
            if (res != null) {
                return res;
            }
            Optional<ComputeTask> optional = this.computeTaskService.findById(id);
            if (!optional.isPresent()) {
                return error(HttpStatus.NOT_FOUND, Constants.NOT_FOUND_DATA_ERROR_EN,
                        Constants.NOT_FOUND_DATA_ERROR_CN);
            }
            ComputeTask task = optional.get();
            // 可以访问此接口的业务系统
            AppSystem bs = isForbidden(vo.getAppkey(), task);
            if (bs == null) {
                return error(HttpStatus.FORBIDDEN, Constants.NO_POWER_MODIFY_THIS_DATA_EN,
                        Constants.NO_POWER_MODIFY_THIS_DATA_CN);
            }
            if (!validSign(bs.getSecret(), req.getRequestURI(), body, sign)) {
                return error(HttpStatus.BAD_REQUEST, Constants.SIGN_ERROR_EN,
                        Constants.SIGN_ERROR_CN);
            }
            if (StringUtils.isBlank(vo.getData())) {
                return error(HttpStatus.BAD_REQUEST, Constants.COMPUTE_TASK_DATA_EMPTY_ERRORR_EN,
                        Constants.COMPUTE_TASK_DATA_EMPTY_ERRORR_CN);
            }
            task.setData(vo.getData());
            task.setUpdateDate(new Date());
            this.computeTaskService.updateData(task);

            String content = getModifyLogBody(mapper, vo.getData(), task);
            addAuditLog(vo.getAppkey(), bs.getName(), ApiType.Compute, OperateType.Modify,
                    task.getName(), content, vo.getAppUserId(), vo.getAppUserName());

            Map<String, Object> map = getComputeMap(task);
            return ok(Constants.MODIFY_SUCCESS_EN, Constants.MODIFY_SUCCESS_CN, map);
        } catch (Exception e) {
            LOG.error(Constants.COMPUTE_TASK_MODIFY_ERROR_EN, e);
            return error(HttpStatus.INTERNAL_SERVER_ERROR, Constants.COMPUTE_TASK_MODIFY_ERROR_EN,
                    Constants.COMPUTE_TASK_MODIFY_ERROR_CN);
        }
    }

    private Map<String, Object> getComputeMap(ComputeTask realTask) {
        Map<String, Object> map = new HashMap<>();
        map.put("id", realTask.getId());
        map.put("name", realTask.getName());
        map.put("data", realTask.getData());
        map.put("description", realTask.getDescription());
        map.put("createDate", realTask.getCreateDate());
        map.put("updateDate", realTask.getUpdateDate());
        return map;
    }

    @DeleteMapping(value = "/api/compute/{id}")
    public ResponseEntity<String> delete(HttpServletRequest req,
                                         @PathVariable("id") String id,
                                         @RequestParam("appkey") String appKey,
                                         @RequestParam("sign") String sign,
                                         @RequestParam("timestamp") Long timestamp,
                                         @RequestParam("appUserId") String userId,
                                         @RequestParam("appUserName") String userName) {
        try {
            ResponseEntity<String> res = validateBase(appKey, userId, userName, timestamp);
            if (res != null) {
                return res;
            }
            // 此接口是否存在
            Optional<ComputeTask> optional = computeTaskService.findById(id);
            if (!optional.isPresent()) {
                return error(HttpStatus.NOT_FOUND, Constants.NOT_FOUND_DATA_ERROR_EN,
                        Constants.NOT_FOUND_DATA_ERROR_CN);
            }
            ComputeTask task = optional.get();
            // 可以访问此计算任务的业务系统
            AppSystem bs = isAdminForbidden(appKey);
            if (bs == null) {
                return error(HttpStatus.FORBIDDEN, Constants.NO_POWER_DELETE_TASK_ERROR_EN,
                        Constants.NO_POWER_DELETE_TASK_ERROR_CN);
            }
            // 签名校验
            Map<String, Object> params = new HashMap<>();
            if (!validSign(bs.getSecret(), req.getRequestURI(), params, appKey, timestamp, sign, userId, userName)) {
                return error(HttpStatus.BAD_REQUEST, Constants.SIGN_ERROR_EN,
                        Constants.SIGN_ERROR_CN);
            }
            // 删除计算任务
            this.computeTaskService.deleteById(id);

            // 记录日志
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(task);
            addAuditLog(appKey, bs.getName(), ApiType.Compute, OperateType.Delete,
                    task.getName(), json, userId, userName);
            return ok(task);
        } catch (Exception e) {
            LOG.error(Constants.API_COMPUTE_ERROR_EN, e);
            return error(HttpStatus.INTERNAL_SERVER_ERROR, Constants.API_COMPUTE_ERROR_EN,
                    Constants.API_COMPUTE_ERROR_CN);
        }
    }

    private AppSystem isAdminForbidden(String appId) throws Exception {
        if (!manageCenterAppId.equalsIgnoreCase(appId)) {
            return null;
        }
        Optional<AppSystem> bs = appSystemService.findById(manageCenterAppId);
        if (bs.isPresent()) {
            AppSystem tmp = bs.get();
            decryptSecret(tmp);
            return tmp;
        }
        return null;
    }

    private AppSystem isForbidden(String appkey, ComputeTask computeTask) throws Exception {
        if (computeTask.getSystems() != null && computeTask.getSystems().size() > 0) {
            for (AppSystem bs : computeTask.getSystems()) {
                if (bs.getId() != null && appkey.equals(bs.getId())) {
                    decryptSecret(bs);
                    return bs;
                }
            }
        }
        return null;
    }

}
